package com.rslai.base.tool.servertest.util;

import com.rslai.base.tool.servertest.annotation.ChildrenConfig;
import com.rslai.base.tool.servertest.annotation.ConfigElement;
import com.rslai.base.tool.servertest.annotation.Property;
import com.rslai.base.tool.servertest.command.CommandFactory;
import com.rslai.base.tool.servertest.config.StepConfig;
import com.rslai.base.tool.servertest.dsl.DSLCommandConfig;
import com.rslai.base.tool.servertest.model.KeyValueStore;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.dom4j.Attribute;

public class ConfigUtils {
    public static Object init(Class clazz, org.dom4j.Element element) {
        Object target = ReflectionUtils.newInstance(clazz);
        setCommandName(target, element.getName());
        setDefaultProperty(target, clazz, element);
        List fields = ReflectionUtils.getAllFields(clazz);
        Iterator i$ = fields.iterator();
        do {
            if (!i$.hasNext())
                break;
            Field field = (Field) i$.next();
            if (!getDefaultProperty(clazz).equals(field.getName()) && !ReflectionUtils.isConstant(field))
                if (target instanceof DSLCommandConfig) {
                    if (field.isAnnotationPresent(com.rslai.base.tool.servertest.annotation.Element.class)) {
                        List params = getAllParams(element);
                        Attribute attribute;
                        for (Iterator iterator = element.attributeIterator(); iterator.hasNext(); params.add(new KeyValueStore(attribute.getName(), attribute.getValue())))
                            attribute = (Attribute) iterator.next();

                        ReflectionUtils.setFieldValue(target, field, params);
                    }
                } else {
                    initProperty(target, element, field);
                    initElementProperty(target, element, field);
                    initConfigChildren(target, element, field);
                }
        } while (true);
        return target;
    }

    public static Object initDataCase(Class clazz, String caseId, String executor) {
        Object target = ReflectionUtils.newInstance(clazz);
        setCommandName(target, executor);
        List fields = ReflectionUtils.getAllFields(clazz);
        Iterator i$ = fields.iterator();
        do {
            if (!i$.hasNext())
                break;
            Field field = (Field) i$.next();
            if ((target instanceof DSLCommandConfig) && field.isAnnotationPresent(com.rslai.base.tool.servertest.annotation.Element.class)) {
                List params = new ArrayList();
                params.add(new KeyValueStore("data", caseId));
                ReflectionUtils.setFieldValue(target, field, params);
            }
        } while (true);
        return target;
    }


    private static <T> void setCommandName(T target, String elementName) {
        if ((target instanceof StepConfig))
            ReflectionUtils.setFieldValue(target, "commandName", elementName);
    }

    private static void setDefaultProperty(Object target, Class clazz, org.dom4j.Element element) {
        if (clazz.isAnnotationPresent(ConfigElement.class)) {
            ConfigElement annotation = (ConfigElement) clazz.getAnnotation(ConfigElement.class);
            String propertyName = annotation.defaultProperty();
            if ((StringUtils.isNotBlank(propertyName)) && (StringUtils.isNotBlank(element.getTextTrim()))) {
                Field field = ReflectionUtils.getField(clazz, propertyName);
                ReflectionUtils.setFieldValue(target, field, getValue(element));
            }
        }
    }

    private static String getDefaultProperty(Class clazz) {
        if (clazz.isAnnotationPresent(ConfigElement.class)) {
            ConfigElement annotation = (ConfigElement) clazz.getAnnotation(ConfigElement.class);
            return annotation.defaultProperty();
        }
        return "";
    }

    private static <T> void initProperty(T target, org.dom4j.Element element, Field field) {
        if (field.isAnnotationPresent(Property.class)) {
            String value = getValueByName(element, field);
            Property property = (Property) field.getAnnotation(Property.class);
            if ((property.required()) && (StringUtils.isBlank(value))) {
                throw new RuntimeException(String.format("必须为%s的%s字段提供值", new Object[]{target.getClass().getName(), field.getName()}));
            }
            ReflectionUtils.setFieldValue(target, field, value);
        }
    }

    private static <T> void initElementProperty(T target, org.dom4j.Element element, Field field) {
        if (field.isAnnotationPresent(com.rslai.base.tool.servertest.annotation.Element.class)) {
            List params = getAllParams(element);
            ReflectionUtils.setFieldValue(target, field, params);
        }
    }

    private static List<KeyValueStore> getAllParams(org.dom4j.Element element) {
        Iterator iterator = element.elementIterator();
        List params = new ArrayList();
        while (iterator.hasNext()) {
            params.addAll(getParams((org.dom4j.Element) iterator.next()));
        }
        return params;
    }

    private static List<KeyValueStore> processList(List<KeyValueStore> keyValueStores) {
        if (keyValueStores == null) return Collections.EMPTY_LIST;
        List keyValueStoreList = new ArrayList();
        for (KeyValueStore keyValueStore : keyValueStores) {
            if ("list".equals(keyValueStore.getName())) {
                Object value = keyValueStore.getValue();
                if (!(value instanceof List)) {
                    keyValueStoreList.add(keyValueStore);
                } else {
                    List valueList = (List) value;
                    if ((valueList != null) && (valueList.size() > 0))
                        keyValueStoreList.add(new KeyValueStore(((KeyValueStore) valueList.get(0)).getName(), keyValueStore.getValue()));
                }
            } else {
                keyValueStoreList.add(keyValueStore);
            }
        }
        return keyValueStoreList;
    }

    private static List<KeyValueStore> getParams(org.dom4j.Element element) {
        if (isListElement(element)) {
            ArrayList list = new ArrayList();
            list.add(new KeyValueStore(element.getName(), getList(element)));
            return processList(list);
        }
        if (hasAttribute(element)) {
            return getAllAttribute(element);
        }
        if (hasChildren(element)) {
            return Arrays.asList(new KeyValueStore[]{new KeyValueStore(element.getName(), getAllChildrenMap(element))});
        }
        if (element.isTextOnly()) {
            return getElementBody(element);
        }
        return Collections.emptyList();
    }

    private static boolean isListElement(org.dom4j.Element element) {
        return (element.getName().equals("list")) && (element.attributeCount() == 0);
    }

    private static Map<String, Object> getAllChildrenMap(org.dom4j.Element element) {
        List result = getList(element);
        return convertKeyValueStoreToMap(result);
    }

    private static List<KeyValueStore> getList(org.dom4j.Element element) {
        Iterator iterator = element.elementIterator();
        List result = new ArrayList();
        while (iterator.hasNext()) {
            org.dom4j.Element child = (org.dom4j.Element) iterator.next();
            List params = getParams(child);
            result.addAll(params);
        }
        return result;
    }

    protected static Map<String, Object> convertKeyValueStoreToMap(List<KeyValueStore> params) {
        Map result = new LinkedHashMap();
        for (KeyValueStore kvs : params) {
            result.put(kvs.getName(), kvs.getValue());
        }
        return result;
    }

    private static <T> void initConfigChildren(T target, org.dom4j.Element element, Field field) {
        if (field.isAnnotationPresent(ChildrenConfig.class))
            ReflectionUtils.setFieldValue(target, field, getChildrenConfig(element));
    }

    private static List<StepConfig> getChildrenConfig(org.dom4j.Element element) {
        Iterator iterator = element.elementIterator();
        List result = new ArrayList();
        while (iterator.hasNext()) {
            org.dom4j.Element configElement = (org.dom4j.Element) iterator.next();
            Class configClass = CommandFactory.getInstance().getConfig(configElement.getName());
            if (configClass == null) {
                configClass = DSLCommandConfig.class;
            }
            StepConfig config = (StepConfig) init(configClass, configElement);
            result.add(config);
        }
        return result;
    }

    protected static List<KeyValueStore> getElementBody(org.dom4j.Element element) {
        String name = element.getName();
        String value = getValue(element);
        return Arrays.asList(new KeyValueStore[]{new KeyValueStore(name, value)});
    }

    private static List<KeyValueStore> getAllAttribute(org.dom4j.Element element) {
        List result = new ArrayList();
        Iterator iterator = element.attributeIterator();
        while (iterator.hasNext()) {
            Attribute attribute = (Attribute) iterator.next();

            if (isNameValuePair(element, attribute))
                result.add(new KeyValueStore(attribute.getValue(), getValue(element)));
            else if (isSingleValue(element, attribute))
                result.add(new KeyValueStore(element.getName(), getValue(attribute)));
            else {
                result.add(new KeyValueStore(attribute.getName(), getValue(attribute)));
            }
        }
        return result;
    }

    private static boolean isSingleValue(org.dom4j.Element element, Attribute attribute) {
        return (element.attributeCount() == 1) && ("value".equals(attribute.getName()));
    }

    private static boolean isNameValuePair(org.dom4j.Element element, Attribute attribute) {
        return (element.attributeCount() == 1) && ("name".equals(attribute.getName()));
    }

    protected static boolean hasChildren(org.dom4j.Element element) {
        List elements = element.elements();
        return (elements != null) && (elements.size() > 0);
    }

    protected static boolean hasAttribute(org.dom4j.Element element) {
        return element.attributeCount() > 0;
    }

    private static String getValueByName(org.dom4j.Element element, Field field) {
        Property annotation = (Property) field.getAnnotation(Property.class);
        String name = getName(field, annotation);
        if (!hasAttribute(element, name).booleanValue()) {
            String value = getChildrenValue(element, name);
            return value == null ? PropertyUtils.replaceConfigValue(annotation.defaultValue()) : value;
        }
        return getAttribute(element, name);
    }

    private static String getChildrenValue(org.dom4j.Element element, String name) {
        org.dom4j.Element e = element.element(name);
        if (e == null) {
            return null;
        }
        return getValue(e);
    }

    private static String getAttribute(org.dom4j.Element node, String name) {
        Attribute attribute = node.attribute(name);
        if (attribute != null) {
            return getValue(attribute);
        }
        return null;
    }

    private static Boolean hasAttribute(org.dom4j.Element element, String name) {
        return getAttribute(element, name) == null ? Boolean.FALSE : Boolean.TRUE;
    }

    private static String getName(Field field, Property annotation) {
        if (StringUtils.isNotBlank(annotation.value())) {
            return annotation.value();
        }
        return field.getName();
    }

    private static String getValue(Attribute attribute) {
        return PropertyUtils.replaceConfigValue(attribute.getValue());
    }

    private static String getValue(org.dom4j.Element element) {
        return PropertyUtils.replaceConfigValue(StringUtils.trim(element.getText()));
    }
}
